{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VAns: 可变结构电路"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Overview"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "变分量子算法 (Variational Quantum Algorithm, VQA) 是一种使用经典优化器来训练参数化量子电路从而最小化特定的损失函数的算法。常见的变分量子算法，例如变分量子本征求解器（Variational Quantum Eigensolvers, VQE）和量子近似优化算法（Quantum Approximate Optimization Algorithm, QAOA），需要一个预设的固定参数化量子电路来完成优化过程。当电路过于简单时，其表达能力不够强，不足以得到目标损失函数的最优值。另一方面，如果电路过于复杂，虽然其表达能力很强，但是会受到贫瘠高原现象（Barren Plateau, BP）的影响，梯度的消失会使得优化器无法得到全局最优的解。所以，我们需要选择合适的电路结构来解决特定的问题，而一个好的电路设计算法就可以帮助我们找到这种电路。\n",
    "\n",
    "在本教程中，我们将介绍一种叫做 VAns 的可变结构电路算法 [1]，这种算法可以针对特定的问题找到一个较浅的电路完成优化过程。从一个简单的初始电路开始，VAns 算法在优化的过程中会不断向电路中添加和删减量子门模块，从而在优化损失函数的同时也能保持较短的电路的深度。我们将以量子本征求解这个问题作为例子来展示 VAns 算法，并和原来使用固定电路结构的 VQE 算法进行对比。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 算法流程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "VAns 算法由如下步骤组成：\n",
    "1. 准备一个简单的初始电路，使用经典优化器对电路参数进行调整，最小化目标损失函数，记录最优值。\n",
    "2. 从一个集合中随机选择量子门模块（如下图所示，各量子门模块仅由 $R_y$ 门、$R_z$ 门，和 $CNOT$ 门组成），随机选择插入模块所作用的量子比特，并将模块添加到电路末尾。添加模块中的量子门的参数都初始化为 $0$ ，这样添加的模块一开始等同于 $I$。\n",
    "\n",
    "![Inserting Blocks](blocks.png)\n",
    "\n",
    "3. 根据下图中的规则化简电路，对化简后的电路进行优化，更新其参数，获得损失函数的当前最优值。比较当前损失函数最优值与上一个记录的最优值，根据设定的阈值决定是否接受新电路。若接受新电路，则继续遍历电路中的量子门，并删除不会降低损失的门，将精简过的电路记为当前电路，并记录其最优损失。若拒绝则直接回到第2步。\n",
    "\n",
    "![Simplification rules](rules.png)\n",
    "\n",
    "4. 重复步骤2-3，达到设定的迭代次数后停止，输出电路和最优损失。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 量桨实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们以求解氢分子的基态能量为例来展示如何在量桨上使用 VAns 算法，这里我们着重展示如何优化和精简电路结构，优化损失函数的过程同[变分量子本征求解器（VQE）](https://qml.baidu.com/tutorials/quantum-simulation/variational-quantum-eigensolver.html)。我们通过下面几行代码引入必要的包。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle\n",
    "import paddle_quantum\n",
    "import paddle_quantum.qchem as qchem\n",
    "import numpy as np\n",
    "from paddle_quantum.ansatz import Circuit\n",
    "from paddle_quantum.ansatz.vans import Inserter, Simplifier, VAns, cir_decompose\n",
    "from paddle_quantum.hamiltonian import Hamiltonian\n",
    "\n",
    "np.random.seed(11)\n",
    "paddle.seed(11)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们首先需要构造氢分子的哈密顿量，具体的说明可见[变分量子本征求解器（VQE）](https://qml.baidu.com/tutorials/quantum-simulation/variational-quantum-eigensolver.html)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "geo = qchem.geometry(structure=[[\"H\", [-0.0, 0.0, 0.0]], [\"H\", [-0.0, 0.0, 0.74]]])\n",
    "# 将分子信息存储在 molecule 里，包括单体积分（one-body integrations），双体积分（two-body integrations），分子的哈密顿量等\n",
    "molecule = qchem.get_molecular_data(\n",
    "    geometry=geo,\n",
    "    basis=\"sto-3g\",\n",
    "    charge=0,\n",
    "    multiplicity=1,\n",
    "    method=\"fci\",\n",
    "    if_save=True,\n",
    "    if_print=True,\n",
    ")\n",
    "# 提取哈密顿量\n",
    "molecular_hamiltonian = qchem.spin_hamiltonian(\n",
    "    molecule=molecule,\n",
    "    filename=None,\n",
    "    multiplicity=1,\n",
    "    mapping_method=\"jordan_wigner\",\n",
    ")\n",
    "# n 为量子比特数\n",
    "n = molecular_hamiltonian.n_qubits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来我们需要定义损失函数，也就是电路输出量子态关于该哈密顿量的期望值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义损失函数\n",
    "def loss_func(cir: Circuit, H: Hamiltonian) -> paddle.Tensor:\n",
    "    return cir().expec_val(H)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在训练前，我们还需要设定一些有关 VAns 算法和优化器的参数。读者可以自行调整这些参数来观察 VAns 算法的变化。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "EPSI = 0.001 # 插入模块初始化参数的浮动范围\n",
    "IR = 1 # 添加模块的速率\n",
    "ITERI = 120 # 参数优化的迭代次数\n",
    "ITERO = 5 # 结构优化的迭代次数\n",
    "LR = 0.1 # 学习率\n",
    "T = 0.01 # 删除量子门时允许损失值上升的范围\n",
    "A = 100 # 对于更新电路的采纳率\n",
    "IS0 = True # 如果初始态为 |0>, 则设为 True\n",
    "\n",
    "paddle_quantum.set_backend('state_vector') # 设置运行模式为态矢量模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 初始化一个 VAns 的模块对象\n",
    "vans = VAns(n, loss_func, molecular_hamiltonian,\n",
    "           epsilon=EPSI,\n",
    "           insert_rate=IR,\n",
    "           iter=ITERI,\n",
    "           iter_out=ITERO,\n",
    "           LR=LR,\n",
    "           threshold=T,\n",
    "           accept_wall=A,\n",
    "           zero_init_state=IS0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 初始电路"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们给出了一个简单的初始电路，其参数是从均匀分布中随机选取的。对于这个初始电路，我们运行优化过程使损失函数最小化。优化过程和普通的 VQE 算法相同，参数 **iter** 和 **LR** 分别决定了优化器迭代次数和学习率。记录优化后的参数化电路以及损失，将其作为结构优化的起始点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\v_zhanglei48\\Anaconda3\\envs\\pq_new\\lib\\site-packages\\paddle\\fluid\\framework.py:744: DeprecationWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence this warning, use `bool` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.\n",
      "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n",
      "  elif dtype == np.bool:\n",
      "c:\\Users\\v_zhanglei48\\Anaconda3\\envs\\pq_new\\lib\\site-packages\\paddle\\tensor\\creation.py:130: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. \n",
      "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n",
      "  if data.dtype == np.object:\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iter: 20 loss: [-0.9960856]\n",
      "iter: 40 loss: [-1.0926806]\n",
      "iter: 60 loss: [-1.11364]\n",
      "iter: 80 loss: [-1.1163325]\n",
      "iter: 100 loss: [-1.1167175]\n",
      "iter: 120 loss: [-1.1167548]\n",
      "当前电路:\n",
      "--Rx(3.144)----Rz(3.512)----*----Rx(6.286)----Rz(3.039)-------------------------------------------------------------x--\n",
      "                            |                                                                                       |  \n",
      "----------------------------x----Rx(4.249)----Rz(3.141)----Rx(4.246)----Rz(5.477)----*------------------------------|--\n",
      "                                                                                     |                              |  \n",
      "--Rx(0.001)----Rz(5.499)----*--------------------------------------------------------x----Rx(3.141)----Rz(2.688)----|--\n",
      "                            |                                                                                       |  \n",
      "----------------------------x----Rx(0.003)----Rz(2.362)----Rx(0.003)----Rz(1.500)-----------------------------------*--\n",
      "                                                                                                                       \n"
     ]
    }
   ],
   "source": [
    "# 优化初始电路\n",
    "itr_loss = vans.optimization(vans.cir) \n",
    "\n",
    "# 更新 vans 的损失值\n",
    "vans.loss = itr_loss\n",
    "\n",
    "# 打印当前电路\n",
    "print(\"当前电路:\\n\" + str(vans.cir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 插入量子门模块"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们从量子门模块集合中随机选取模块，随机选取插入模块所作用的量子比特，并将模块插入到当前电路中。注意新插入模块中的量子门的参数都被设置为 $0$，这样添加模块前后的电路实际上是相同的，也就是说更新后的电路可以继承之前电路的损失。下面的代码展示了如何在电路中插入模块。一次插入模块的数量由参数 **insert_rate** 决定。另一个参数 **epsilon** 是用来设定插入模块初始参数与 $0$ 之间的差别。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "添加过后的电路:\n",
      "--Rx(3.144)----Rz(3.512)----*----Rx(6.286)----Rz(3.039)----------------------------------------------------------------------------------------------------x--\n",
      "                            |                                                                                                                              |  \n",
      "----------------------------x----Rz(0.000)----Rx(0.000)----Rz(-0.00)----Rx(4.249)----Rz(3.141)----Rx(4.246)----Rz(5.477)----*------------------------------|--\n",
      "                                                                                                                            |                              |  \n",
      "--Rx(0.001)----Rz(5.499)----*-----------------------------------------------------------------------------------------------x----Rx(3.141)----Rz(2.688)----|--\n",
      "                            |                                                                                                                              |  \n",
      "----------------------------x----Rx(0.003)----Rz(2.362)----Rx(0.003)----Rz(1.500)--------------------------------------------------------------------------*--\n",
      "                                                                                                                                                              \n"
     ]
    }
   ],
   "source": [
    "# 向电路中添加模块，在这么做之前需要将电路从 layer 分解为量子门\n",
    "new_cir = cir_decompose(vans.cir)\n",
    "new_cir = Inserter.insert_identities(new_cir, vans.insert_rate, vans.epsilon)\n",
    "\n",
    "# 打印更新过后的电路\n",
    "print(\"添加过后的电路:\\n\" + str(new_cir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 简化电路"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来我们需要化简当前的量子电路。化简电路的规则十分简单：\n",
    "1. 将连续的 $CNOT$ 门合并消除；\n",
    "2. 将连续的旋转门合并起来；\n",
    "3. 若电路初始态为 $|0\\rangle$，删除电路前端的 $CNOT$ 门 和 $Rz$ 门；\n",
    "4. 交换旋转门和 $CNOT$ 门的位置以便进一步化简。\n",
    "\n",
    "化简电路后，我们对电路中的参数进行优化以最小化损失函数，得到更新参数后的电路和对应的损失值。如果新的损失值小于之前记录的损失值，我们就接受这个电路作为新电路。如果新损失值大于之前的损失值，那么我们以一定概率接受这个电路，接受的概率与参数 **accept_wall** 有关。为了进一步简化电路，在接受新电路后，我们还会遍历电路中的量子门，如果删除某个量子门后，损失不会降低或者只会提高一个小于 **threshold** 的值，那么我们将该量子门删除，否则将其保留。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\v_zhanglei48\\Anaconda3\\envs\\pq_new\\lib\\site-packages\\paddle\\fluid\\dygraph\\math_op_patch.py:251: UserWarning: The dtype of left and right variables are not the same, left dtype is paddle.float64, but right dtype is paddle.float32, the right dtype will convert to paddle.float64\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--Rx(3.144)----Rz(3.512)----*----Rx(6.286)----Rz(3.039)------------------------------------------------x--\n",
      "                            |                                                                          |  \n",
      "----------------------------x----Rz(5.962)----Rx(5.858)----Rz(9.426)----*------------------------------|--\n",
      "                                                                        |                              |  \n",
      "--Rx(0.001)----Rz(5.499)----*-------------------------------------------x----Rx(3.141)----Rz(2.688)----|--\n",
      "                            |                                                                          |  \n",
      "----------------------------x----Rz(3.604)----Rx(5.126)----Rz(4.462)-----------------------------------*--\n",
      "                                                                                                          \n",
      "iter: 20 loss: [-1.1040964]\n",
      "iter: 40 loss: [-1.1159219]\n",
      "iter: 60 loss: [-1.1165943]\n",
      "iter: 80 loss: [-1.116723]\n",
      "iter: 100 loss: [-1.1167536]\n",
      "iter: 120 loss: [-1.1167593]\n",
      "Accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "      12  gates are deleted!\n"
     ]
    }
   ],
   "source": [
    "# 根据简化规则简化电路\n",
    "new_cir = Simplifier.simplify_circuit(new_cir, vans.zero_init_state)\n",
    "\n",
    "# 打印简化过后的电路\n",
    "print(new_cir)\n",
    "\n",
    "# 对简化过后的电路进行优化\n",
    "itr_loss = vans.optimization(new_cir)\n",
    "\n",
    "# 计算损失值的变化\n",
    "relative_diff = (itr_loss - vans.loss) / np.abs(itr_loss)\n",
    "\n",
    "# 若损失值降低或升高的幅度不大于设定的阈值，那么就接受电路\n",
    "if relative_diff <= 0 or np.random.random() <= np.exp(\n",
    "    -relative_diff * vans.accept_wall\n",
    "):\n",
    "    print(\"Accpet the new circuit!\")\n",
    "\n",
    "    # 移除不会降低损失的量子门\n",
    "    new_cir = vans.delete_gates(new_cir, itr_loss)\n",
    "    new_cir = Simplifier.simplify_circuit(new_cir, vans.zero_init_state)\n",
    "    itr_loss = loss_func(new_cir, *vans.loss_args)\n",
    "    vans.loss = itr_loss\n",
    "else:\n",
    "    print(\"Decline the new circuit!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "简化后的电路如下所示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前电路为:\n",
      "--Rx(3.141)----*----------------------x--\n",
      "               |                      |  \n",
      "---------------x----*-----------------|--\n",
      "                    |                 |  \n",
      "--------------------x----Rx(3.142)----|--\n",
      "                                      |  \n",
      "--------------------------------------*--\n",
      "                                         \n"
     ]
    }
   ],
   "source": [
    "# 更新当前电路\n",
    "vans.cir = new_cir\n",
    "\n",
    "print(\"当前电路为:\\n\" + str(vans.cir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们将上述的插入量子门模块和简化电路的步骤一起作为电路结构优化的一次迭代，总的迭代次数由参数 **iter_out** 决定。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 简化版本"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述过程清晰明了地展示了 VAns 的运行步骤和原理，包括了量子门模块添加以及电路简化的过程，然而实际在量桨中使用 VAns 可以不必了解这些繁杂的步骤，因为量桨提供了一个经过封装的 VAns 算法，以便用户进行调用。下面的一行代码可以完成所有的电路结构优化以及参数训练过程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Out iteration 1 for structure optimization:\n",
      "iter: 20 loss: [-1.1167214]\n",
      "iter: 40 loss: [-1.1166948]\n",
      "iter: 60 loss: [-1.1167494]\n",
      "iter: 80 loss: [-1.116759]\n",
      "iter: 100 loss: [-1.1167591]\n",
      "iter: 120 loss: [-1.1167595]\n",
      " Current loss: [-1.1167595]\n",
      " Current cir:\n",
      "--Rx(3.141)----*----------------------x--\n",
      "               |                      |  \n",
      "---------------x----*-----------------|--\n",
      "                    |                 |  \n",
      "--------------------x----Rx(3.141)----|--\n",
      "                                      |  \n",
      "--------------------------------------*--\n",
      "                                          \n",
      "\n",
      "Out iteration 2 for structure optimization:\n",
      "iter: 20 loss: [-1.008932]\n",
      "iter: 40 loss: [-1.1085335]\n",
      "iter: 60 loss: [-1.133744]\n",
      "iter: 80 loss: [-1.1368372]\n",
      "iter: 100 loss: [-1.1372123]\n",
      "iter: 120 loss: [-1.1372803]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "      3  gates are deleted!\n",
      " Current loss: -1.1331816911697388\n",
      " Current cir:\n",
      "--Rx(3.143)----*------------------------------------------------------------------x--\n",
      "               |                                                                  |  \n",
      "---------------x----*----Rx(0.223)----Rz(2.485)----*--------*---------------------|--\n",
      "                    |                              |        |                     |  \n",
      "--------------------|------------------------------|--------x--------Rx(3.142)----|--\n",
      "                    |                              |                              |  \n",
      "--------------------x------------------------------x----Rz(0.458)-----------------*--\n",
      "                                                                                      \n",
      "\n",
      "Out iteration 3 for structure optimization:\n",
      "iter: 20 loss: [-0.37809896]\n",
      "iter: 40 loss: [-1.0471339]\n",
      "iter: 60 loss: [-1.1262195]\n",
      "iter: 80 loss: [-1.1365637]\n",
      "iter: 100 loss: [-1.1371676]\n",
      "iter: 120 loss: [-1.1372685]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "      4  gates are deleted!\n",
      " Current loss: -1.1282802820205688\n",
      " Current cir:\n",
      "--Rx(3.143)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.224)----Rz(2.252)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.146)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                              \n",
      "\n",
      "Out iteration 4 for structure optimization:\n",
      "iter: 20 loss: [-0.5603936]\n",
      "iter: 40 loss: [-1.0798837]\n",
      "iter: 60 loss: [-1.1325867]\n",
      "iter: 80 loss: [-1.1360469]\n",
      "iter: 100 loss: [-1.1370715]\n",
      "iter: 120 loss: [-1.1372685]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "      9  gates are deleted!\n",
      " Current loss: -1.1321836709976196\n",
      " Current cir:\n",
      "--Rx(3.145)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.228)----Rz(2.075)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.143)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                              \n",
      "\n",
      "Out iteration 5 for structure optimization:\n",
      "iter: 20 loss: [-1.136969]\n",
      "iter: 40 loss: [-1.1371931]\n",
      "iter: 60 loss: [-1.137272]\n",
      "iter: 80 loss: [-1.1372828]\n",
      "iter: 100 loss: [-1.1372837]\n",
      "iter: 120 loss: [-1.1372838]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "      2  gates are deleted!\n",
      " Current loss: -1.1372729539871216\n",
      " Current cir:\n",
      "--Rx(3.142)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.226)----Rz(1.571)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.149)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                              \n",
      "\n",
      "\n",
      "\n",
      "The final loss: -1.1372729539871216\n",
      "The final circuit:\n",
      "--Rx(3.142)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.226)----Rz(1.571)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.149)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                             \n"
     ]
    }
   ],
   "source": [
    "# 使用 VAns 中内置的 train() 函数直接完成整个训练过程\n",
    "vans.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最终 VAns 算法给出的电路如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最终电路:\n",
      "--Rx(3.142)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.226)----Rz(1.571)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.149)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                             \n",
      "最终损失:\n",
      "-1.1372729539871216\n"
     ]
    }
   ],
   "source": [
    "print(\"最终电路:\\n\" + str(vans.cir))\n",
    "print(\"最终损失:\\n\" + str(vans.loss))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 与原始 VQE 的对比"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过上面的结果我们不难发现，通过 VAns 得到的电路仅含有5个参数，电路的深度为9，得到的最小损失值为 $-1.13728392$ Ha。而原始的 VQE 算法（见[变分量子本征求解器（VQE）](https://qml.baidu.com/tutorials/quantum-simulation/variational-quantum-eigensolver.html)）则需要一个含12个参数且深度为11的电路，由此可见 VAns 可以极大的减少电路中的参数数量并保持较浅的的电路深度。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "_______\n",
    "\n",
    "## 参考文献\n",
    "\n",
    "[1] Bilkis, M., et al. \"A semi-agnostic ansatz with variable structure for quantum machine learning.\" [arXiv preprint arXiv:2103.06712 (2021).](https://arxiv.org/abs/2103.06712)"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "2ab84abaf8d5bbc8765aba8eb82d11e7069f2ff20e8f79b8a9cdeccefd2ac4da"
  },
  "kernelspec": {
   "display_name": "Python 3.8.13 ('pq_new')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
